/**
 ****************************************************************************************************
 * @file        lwip_comm.h
 * @author      正点原子团队(ALIENTEK)
 * @version     V1.0
 * @date        2022-08-01
 * @brief       lwIP配置驱动
 * @license     Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
 ****************************************************************************************************
 * @attention
 *
 * 实验平台:正点原子 阿波罗 F429开发板
 * 在线视频:www.yuanzige.com
 * 技术论坛:www.openedv.com
 * 公司网址:www.alientek.com
 * 购买地址:openedv.taobao.com
 *
 * 修改说明
 * V1.0 20211202
 * 第一次发布
 *
 ****************************************************************************************************
 */

#include "lwip_comm.h"
#include "netif/etharp.h"
#include "lwip/dhcp.h"
#include "lwip/mem.h"
#include "lwip/memp.h"
#include "lwip/init.h"
#include "ethernetif.h"
#include "lwip/timeouts.h"
#include "lwip/tcpip.h"
#include "malloc.h"
#include "usart.h"
#include "ethernet_chip.h"
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>


#define LWIP_USING_DHCP 0
__lwip_dev g_lwipdev;
struct netif g_lwip_netif;

#if LWIP_USING_DHCP
uint32_t g_dhcp_fine_timer = 0;
__IO uint8_t g_lwip_dhcp_state = LWIP_DHCP_OFF;
#endif

#define LWIP_LINK_TASK_PRIO             1
#define LWIP_LINK_STK_SIZE              128 * 2
void lwip_link_thread( void * argument );


#define LWIP_DHCP_TASK_PRIO             0
#define LWIP_DHCP_STK_SIZE              128 * 2
void lwip_link_status_updated(struct netif *netif);



void lwip_comm_default_ip_set(__lwip_dev *lwipx)
{
    lwipx->mac[0] = 0x00;
    lwipx->mac[1] = 0x80;
    lwipx->mac[2] = 0xe1;
    lwipx->mac[3] = 0x14;
    lwipx->mac[4] = 0x2e;
    lwipx->mac[5] = 0x34;

    /* 默认本地IP为:192.168.1.30 */
    lwipx->ip[0] = 192;
    lwipx->ip[1] = 168;
    lwipx->ip[2] = 2;
    lwipx->ip[3] = 129;
    /* 默认子网掩码:255.255.255.0 */
    lwipx->netmask[0] = 255;
    lwipx->netmask[1] = 255;
    lwipx->netmask[2] = 255;
    lwipx->netmask[3] = 0;
    lwipx->dhcpstatus = 0; /* 没有DHCP */
}


uint8_t lwip_comm_init(void)
{
    struct netif *netif_init_flag;
    ip_addr_t ipaddr;
    ip_addr_t netmask;
    ip_addr_t gw;

    tcpip_init(NULL, NULL);

    lwip_comm_default_ip_set(&g_lwipdev);

#if LWIP_USING_DHCP
    ip_addr_set_zero_ip4(&ipaddr);
    ip_addr_set_zero_ip4(&netmask);
    ip_addr_set_zero_ip4(&gw);
#else
    IP4_ADDR(&ipaddr, g_lwipdev.ip[0], g_lwipdev.ip[1], g_lwipdev.ip[2], g_lwipdev.ip[3]);
    IP4_ADDR(&netmask, g_lwipdev.netmask[0], g_lwipdev.netmask[1], g_lwipdev.netmask[2], g_lwipdev.netmask[3]);
    IP4_ADDR(&gw, g_lwipdev.gateway[0], g_lwipdev.gateway[1], g_lwipdev.gateway[2], g_lwipdev.gateway[3]);
#endif
    netif_init_flag = netif_add(&g_lwip_netif,
                                (const ip_addr_t *)&ipaddr,
                                (const ip_addr_t *)&netmask,
                                (const ip_addr_t *)&gw,
                                NULL,
                                &ethernetif_init,
                                &tcpip_input);


    if (netif_init_flag == NULL)
    {
        return 1;
    }
    else
    {
        netif_set_default(&g_lwip_netif);

#if LWIP_NETIF_LINK_CALLBACK
        lwip_link_status_updated(&g_lwip_netif);
        netif_set_link_callback(&g_lwip_netif, lwip_link_status_updated);

        sys_thread_new("eth_link",
                       lwip_link_thread,
                       &g_lwip_netif,
                       LWIP_LINK_STK_SIZE,
                       LWIP_LINK_TASK_PRIO);
#endif

    }
    g_lwipdev.link_status = LWIP_LINK_OFF;
#if LWIP_USING_DHCP
    g_lwipdev.dhcpstatus = 0;

    sys_thread_new("eth_dhcp",
                   lwip_periodic_handle,
                   &g_lwip_netif,
                   LWIP_DHCP_STK_SIZE,
                   LWIP_DHCP_TASK_PRIO);
#else
        g_lwipdev.dhcpstatus = 0XFF;
        IP4_ADDR(&(g_lwip_netif.ip_addr), g_lwipdev.ip[0], g_lwipdev.ip[1], g_lwipdev.ip[2], g_lwipdev.ip[3]);
        IP4_ADDR(&(g_lwip_netif.netmask), g_lwipdev.netmask[0], g_lwipdev.netmask[1], g_lwipdev.netmask[2], g_lwipdev.netmask[3]);
        IP4_ADDR(&(g_lwip_netif.gw), g_lwipdev.gateway[0], g_lwipdev.gateway[1], g_lwipdev.gateway[2], g_lwipdev.gateway[3]);
        netif_set_addr(&g_lwip_netif, &g_lwip_netif.ip_addr, &g_lwip_netif.netmask, &g_lwip_netif.gw);
        g_lwipdev.lwip_display_fn(2);
#endif
    return 0;
}


void lwip_link_status_updated(struct netif *netif)
{
    if (netif_is_up(netif))
    {
#if LWIP_USING_DHCP
        /* Update DHCP state machine */
        g_lwip_dhcp_state = LWIP_DHCP_START;
        printf ("The network cable is connected \r\n");
#endif /* LWIP_USING_DHCP */
    }
    else
    {
#if LWIP_USING_DHCP
        /* Update DHCP state machine */
        g_lwip_dhcp_state = LWIP_DHCP_LINK_DOWN;
        printf ("The network cable is not connected \r\n");
#endif /* LWIP_USING_DHCP */
    }
}


#if LWIP_USING_DHCP

void lwip_periodic_handle(void *argument)
{
    struct netif *netif = (struct netif *) argument;
    uint32_t ip = 0;
    uint32_t netmask = 0;
    uint32_t gw = 0;
    struct dhcp *dhcp;

    while (1)
    {
        switch (g_lwip_dhcp_state)
        {
            case LWIP_DHCP_START:
            {
                ip_addr_set_zero_ip4(&netif->ip_addr);
                ip_addr_set_zero_ip4(&netif->netmask);
                ip_addr_set_zero_ip4(&netif->gw);
                ip_addr_set_zero_ip4(&netif->ip_addr);
                ip_addr_set_zero_ip4(&netif->netmask);
                ip_addr_set_zero_ip4(&netif->gw);

                g_lwip_dhcp_state = LWIP_DHCP_WAIT_ADDRESS;
                dhcp_start(netif);
            }
            break;
            case LWIP_DHCP_WAIT_ADDRESS:
            {
                if (dhcp_supplied_address(netif))
                {
                    g_lwip_dhcp_state = LWIP_DHCP_ADDRESS_ASSIGNED;

                    ip = g_lwip_netif.ip_addr.addr;
                    netmask = g_lwip_netif.netmask.addr;
                    gw = g_lwip_netif.gw.addr;
                    if (ip != 0)
                    {
                        g_lwipdev.dhcpstatus = 2;
                        g_lwipdev.ip[3] = (uint8_t)(ip >> 24);
                        g_lwipdev.ip[2] = (uint8_t)(ip >> 16);
                        g_lwipdev.ip[1] = (uint8_t)(ip >> 8);
                        g_lwipdev.ip[0] = (uint8_t)(ip);

                        g_lwipdev.netmask[3] = (uint8_t)(netmask >> 24);
                        g_lwipdev.netmask[2] = (uint8_t)(netmask >> 16);
                        g_lwipdev.netmask[1] = (uint8_t)(netmask >> 8);
                        g_lwipdev.netmask[0] = (uint8_t)(netmask);
                        g_lwipdev.gateway[3] = (uint8_t)(gw >> 24);
                        g_lwipdev.gateway[2] = (uint8_t)(gw >> 16);
                        g_lwipdev.gateway[1] = (uint8_t)(gw >> 8);
                        g_lwipdev.gateway[0] = (uint8_t)(gw);
                        g_lwipdev.lwip_display_fn(2);
                    }
                }
                else
                {
                    dhcp = (struct dhcp *)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP);

                    /* DHCP timeout */
                    if (dhcp->tries > LWIP_MAX_DHCP_TRIES)
                    {
                        g_lwip_dhcp_state = LWIP_DHCP_TIMEOUT;
                        g_lwipdev.dhcpstatus = 0XFF;
                        IP4_ADDR(&(g_lwip_netif.ip_addr), g_lwipdev.ip[0], g_lwipdev.ip[1], g_lwipdev.ip[2], g_lwipdev.ip[3]);
                        IP4_ADDR(&(g_lwip_netif.netmask), g_lwipdev.netmask[0], g_lwipdev.netmask[1], g_lwipdev.netmask[2], g_lwipdev.netmask[3]);
                        IP4_ADDR(&(g_lwip_netif.gw), g_lwipdev.gateway[0], g_lwipdev.gateway[1], g_lwipdev.gateway[2], g_lwipdev.gateway[3]);
                        netif_set_addr(netif, &g_lwip_netif.ip_addr, &g_lwip_netif.netmask, &g_lwip_netif.gw);
                        g_lwipdev.lwip_display_fn(2);
                    }
                }
            }
            break;
            case LWIP_DHCP_LINK_DOWN:
            {
                g_lwip_dhcp_state = LWIP_DHCP_OFF;
            }
            break;
            default: break;
        }

        /* wait 1000 ms */
        vTaskDelay(1000);
    }
}
#endif


void lwip_link_thread( void * argument )
{
    uint32_t regval = 0;
    struct netif *netif = (struct netif *) argument;
    int link_again_num = 0;

    while(1)
    {

        HAL_ETH_ReadPHYRegister(&g_eth_handler, ETH_CHIP_ADDR,ETH_CHIP_BSR, &regval);


        if((regval & ETH_CHIP_BSR_LINK_STATUS) == 0)
        {
            g_lwipdev.link_status = LWIP_LINK_OFF;

            link_again_num ++ ;

            if (link_again_num >= 2)
            {
                continue;
            }
            else
            {
#if LWIP_USING_DHCP
                g_lwip_dhcp_state = LWIP_DHCP_LINK_DOWN;
#endif
                HAL_ETH_Stop_IT(&g_eth_handler);
                netif_set_down(netif);
                netif_set_link_down(netif);
            }
        }
        else
        {
            link_again_num = 0;

            if (g_lwipdev.link_status == LWIP_LINK_OFF)
            {
                g_lwipdev.link_status = LWIP_LINK_ON;
                HAL_ETH_Start_IT(&g_eth_handler);
                netif_set_up(netif);
                netif_set_link_up(netif);
            }
        }

        vTaskDelay(100);
    }
}
